package com.ideaaedi.zoo.example.springboot.flowable.sharding.listener;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ideaaedi.commonds.time.DateTimeConverter;
import com.ideaaedi.zoo.diy.artifact.dynamic.datasource.datasource.DatasourceSwitcher;
import com.ideaaedi.zoo.example.springboot.flowable.sharding.constant.DataSourceName;
import com.ideaaedi.zoo.example.springboot.flowable.sharding.entity.po.*;
import com.ideaaedi.zoo.example.springboot.flowable.sharding.event.SyncFlowableProcessHisEvent;
import com.ideaaedi.zoo.example.springboot.flowable.sharding.service.*;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

/**
 * flowable历史数据同步事件
 *
 * @author <font size = "20" color = "#3CAA3C"><a href="https://gitee.com/JustryDeng">JustryDeng</a></font> <img
 * src="https://gitee.com/JustryDeng/shared-files/raw/master/JustryDeng/avatar.jpg" />
 * @since 1.0.0
 */
@Slf4j
@Component
public class SyncFlowableProcessHisListener {
    /**
     * 项目默认时区
     */
    private static final ZoneId DEFAULT_ZONE = ZoneId.of("+8");
    
    @Resource
    private ActHiActinstService actHiActinstService;
    @Resource
    private ActHiAttachmentService actHiAttachmentService;
    @Resource
    private ActHiCommentService actHiCommentService;
    @Resource
    private ActHiDetailService actHiDetailService;
    ///@Resource
    ///private ActHiEntitylinkService actHiEntitylinkService;
    @Resource
    private ActHiIdentitylinkService actHiIdentitylinkService;
    @Resource
    private ActHiProcinstService actHiProcinstService;
    @Resource
    private ActHiTaskinstService actHiTaskinstService;
    @Resource
    private ActHiTskLogService actHiTskLogService;
    @Resource
    private ActHiVarinstService actHiVarinstService;
    
    /**
     * <pre>
     *  注：考虑到复杂度&数据一致性，这里使用同步监听
     *
     *  注：@DS在@Transactional中会切换失败，在@DSTransactional中能切换成功，
     *     因为这里使用的是同步监听，所以如果外层发布事件的方法也需要使用事务管理的话，请使用@DSTransactional，而不使用Transactional
     * </pre>
     */
    @EventListener
    @DS(DataSourceName.PRIMARY)
    @DSTransactional(rollbackFor = Exception.class)
    public void syncFlowableHisData(SyncFlowableProcessHisEvent event) {
        long startTime = System.currentTimeMillis();
        String processInstanceId = event.getProcessInstanceId();
        // ActHiProcinstPO
        List<ActHiProcinstPO> actHiProcinstList = actHiProcinstService.list(
                Wrappers.query(ActHiProcinstPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiProcinstPO::getId, processInstanceId)
        );
        // ActHiActinstPO
        List<ActHiActinstPO> actHiActinstList = actHiActinstService.list(
                Wrappers.query(ActHiActinstPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiActinstPO::getProcInstId, processInstanceId)
        );
        // ActHiAttachmentPO
        List<ActHiAttachmentPO> actHiAttachmentList = actHiAttachmentService.list(
                Wrappers.query(ActHiAttachmentPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiAttachmentPO::getProcInstId, processInstanceId)
        );
        // ActHiCommentPO
        List<ActHiCommentPO> actHiCommentList = actHiCommentService.list(
                Wrappers.query(ActHiCommentPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiCommentPO::getProcInstId, processInstanceId)
        );
        // ActHiDetailPO
        List<ActHiDetailPO> actHiDetailList = actHiDetailService.list(
                Wrappers.query(ActHiDetailPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiDetailPO::getProcInstId, processInstanceId)
        );
        // ActHiIdentitylinkPO
        List<ActHiIdentitylinkPO> actHiIdentitylinkList = actHiIdentitylinkService.list(
                Wrappers.query(ActHiIdentitylinkPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiIdentitylinkPO::getProcInstId, processInstanceId)
        );
        // ActHiTaskinstPO
        List<ActHiTaskinstPO> actHiTaskinstList = actHiTaskinstService.list(
                Wrappers.query(ActHiTaskinstPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiTaskinstPO::getProcInstId, processInstanceId)
        );
        // ActHiTaskinstPO
        List<ActHiTskLogPO> actHiTskLogList = actHiTskLogService.list(
                Wrappers.query(ActHiTskLogPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiTskLogPO::getProcInstId, processInstanceId)
        );
        // ActHiVarinstPO
        List<ActHiVarinstPO> actHiVarinstList = actHiVarinstService.list(
                Wrappers.query(ActHiVarinstPO.class)
                        .select("*, null as sharding_date")
                        .lambda()
                        .eq(ActHiVarinstPO::getProcInstId, processInstanceId)
        );
        
        DatasourceSwitcher.switchTo(DataSourceName.SHARDING).voidExec(() -> {
            // 获取数据分片日期
            Date processStartTime = actHiProcinstList.get(0).getStartTime();
            String yyyyMMdd = DateTimeConverter.REGULAR_DATE.convert2Str(
                    LocalDateTime.ofInstant(processStartTime.toInstant(), DEFAULT_ZONE)
            );
            // ActHiProcinstPO
            actHiProcinstService.remove(
                    Wrappers.lambdaQuery(ActHiProcinstPO.class)
                            .eq(ActHiProcinstPO::getId, processInstanceId)
                            .ge(ActHiProcinstPO::getShardingDate, yyyyMMdd)
                            .le(ActHiProcinstPO::getShardingDate, yyyyMMdd)
            );
            actHiProcinstList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiProcinstService.saveBatch(actHiProcinstList);
            // ActHiActinstPO
            actHiActinstService.remove(
                    Wrappers.lambdaQuery(ActHiActinstPO.class)
                            .eq(ActHiActinstPO::getProcInstId, processInstanceId)
                            .ge(ActHiActinstPO::getShardingDate, yyyyMMdd)
                            .le(ActHiActinstPO::getShardingDate, yyyyMMdd)
            );
            actHiActinstList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiActinstService.saveBatch(actHiActinstList);
            // ActHiAttachmentPO
            actHiAttachmentService.remove(
                    Wrappers.lambdaQuery(ActHiAttachmentPO.class)
                            .eq(ActHiAttachmentPO::getProcInstId, processInstanceId)
                            .ge(ActHiAttachmentPO::getShardingDate, yyyyMMdd)
                            .le(ActHiAttachmentPO::getShardingDate, yyyyMMdd)
            );
            actHiAttachmentList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiAttachmentService.saveBatch(actHiAttachmentList);
            // ActHiCommentPO
            actHiCommentService.remove(
                    Wrappers.lambdaQuery(ActHiCommentPO.class)
                            .eq(ActHiCommentPO::getProcInstId, processInstanceId)
                            .ge(ActHiCommentPO::getShardingDate, yyyyMMdd)
                            .le(ActHiCommentPO::getShardingDate, yyyyMMdd)
            );
            actHiCommentList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiCommentService.saveBatch(actHiCommentList);
            // ActHiDetailPO
            actHiDetailService.remove(
                    Wrappers.lambdaQuery(ActHiDetailPO.class)
                            .eq(ActHiDetailPO::getProcInstId, processInstanceId)
                            .ge(ActHiDetailPO::getShardingDate, yyyyMMdd)
                            .le(ActHiDetailPO::getShardingDate, yyyyMMdd)
            );
            actHiDetailList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiDetailService.saveBatch(actHiDetailList);
            // ActHiIdentitylinkPO
            actHiIdentitylinkService.remove(
                    Wrappers.lambdaQuery(ActHiIdentitylinkPO.class)
                            .eq(ActHiIdentitylinkPO::getProcInstId, processInstanceId)
                            .ge(ActHiIdentitylinkPO::getShardingDate, yyyyMMdd)
                            .le(ActHiIdentitylinkPO::getShardingDate, yyyyMMdd)
            );
            actHiIdentitylinkList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiIdentitylinkService.saveBatch(actHiIdentitylinkList);
            // ActHiTaskinstPO
            actHiTaskinstService.remove(
                    Wrappers.lambdaQuery(ActHiTaskinstPO.class)
                            .eq(ActHiTaskinstPO::getProcInstId, processInstanceId)
                            .ge(ActHiTaskinstPO::getShardingDate, yyyyMMdd)
                            .le(ActHiTaskinstPO::getShardingDate, yyyyMMdd)
            );
            actHiTaskinstList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiTaskinstService.saveBatch(actHiTaskinstList);
            // ActHiTaskinstPO
            actHiTskLogService.remove(
                    Wrappers.lambdaQuery(ActHiTskLogPO.class)
                            .eq(ActHiTskLogPO::getProcInstId, processInstanceId)
                            .ge(ActHiTskLogPO::getShardingDate, yyyyMMdd)
                            .le(ActHiTskLogPO::getShardingDate, yyyyMMdd)
            );
            actHiTskLogList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiTskLogService.saveBatch(actHiTskLogList);
            // ActHiVarinstPO
            actHiVarinstService.remove(
                    Wrappers.lambdaQuery(ActHiVarinstPO.class)
                            .eq(ActHiVarinstPO::getProcInstId, processInstanceId)
                            .ge(ActHiVarinstPO::getShardingDate, yyyyMMdd)
                            .le(ActHiVarinstPO::getShardingDate, yyyyMMdd)
            );
            actHiVarinstList.forEach(x -> x.setShardingDate(yyyyMMdd));
            actHiVarinstService.saveBatch(actHiVarinstList);
        });
        long endTime = System.currentTimeMillis();
        log.info("sync flowable his data consume -> {}ms.", endTime - startTime);
    }
    
}
